#include <cxxomfort/cxxomfort.hpp>
#include <cxxomfort/functional.hpp> // hash
#include <cxxomfort/string.hpp>
#include <cxxomfort/library/functional.hpp>
#include <cxxomfort/library/string.hpp>
#include <cxxomfort/library/type_name.hpp>
#include <functional>
#include <iostream>

template <typename T>
void print_hash (T const& t) {
	using namespace std;
	hash<T> H;
    //cout<< "hash is: "<< cxxomfort::typeid_demangle(typeid(H))<< " "<< H.uses_std<< " \n";
	cout<< cxxomfort::library::type_name<T>()<< " -> "<< H(t)<< endl;
}

template <typename T>
void print_xhash (T const& t) {
    using cxxomfort::fix::hash;
	using namespace std;
	hash<T> H;
    //cout<< "hash is: "<< cxxomfort::typeid_demangle(typeid(H))<< " "<< H.uses_std<< " \n";
	cout<< cxxomfort::library::type_name<T>()<< " -> "<< H(t)<< endl;
}


// of note, c++20 adds this:
// https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_ltlt
template <typename Ch, typename T, typename D>
std::basic_ostream<Ch>& operator<< (std::basic_ostream<Ch> &os, std::unique_ptr<T,D> const& p) CXXO_NOEXCEPTNOTHROW {
	void* const x = static_cast<void* const>(p.get());
	return os<< x;
}

enum enum_foo_t { eval1, eval2, eval3 };

int main() {
	using namespace std;
	namespace CLF = cxxomfort::library::functional;
    cxxomfort::output_info(); cout<< "\n"<< endl;

	cout<< "**Demonstration of std::hash< T >\n";
	cout<< "hash< type > : type value -> hash value\n"<< endl;

	// c++03
    cout<< "std::hash for native types and integers: \n"<< endl;
	print_xhash<signed char>('c');
	print_xhash<int>(100);
	print_xhash<float>(-4.99);
	print_xhash<uintmax_t>(1234567890123456789);
	print_xhash<long*>(reinterpret_cast<long*>(0x33dd));
	print_xhash<double*>(reinterpret_cast<double*>(0x0));

	// c++11

    cout<< "std::hash spec-ions introduced in C++11: \n"<< endl;

    cout<< "char(n) types: \n";
    print_xhash<char16_t>('S');
    print_xhash<char32_t>('L');

    cout<< "string types: \n";
	print_xhash<string>("Hello world");
	print_xhash<wstring>(L"Hello wide world");
    print_xhash<u32string>(u32string(17, 0x2322));

    cout<< "others: \n";
	unique_ptr<long> u (new long(0x133));
	print_xhash< unique_ptr<long> >( u );

	// print_hash< error_code > (e);

    cout<< "\n" "std::hash spec-ions introduced in C++14:"<< endl;

    typedef underlying_type<enum_foo_t>::type enum_foo_t_ut;
    print_xhash<enum_foo_t>(eval2);
    print_xhash<enum_foo_t_ut>(0);

	cout<< endl;
    cout<< is_same<uint16_t,char16_t>::value<< endl;
    
}
